﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;

namespace Elvex
{
    public class NetProtocol
    {
        private Socket m_socket;

        public NetProtocol(Socket socket)
        {
            m_socket = socket;
        }

        public Action<byte[]> OnReceive { get; set; }
        
        public Action<Exception> OnError { get; set; }

        #region Write
        private Queue<byte[]> WriteQueue { get; } = new Queue<byte[]>();
        /// <summary>
        /// 读取偏移值
        /// </summary>
        private int WriteOffset { get; set; } = 0;

        /// <summary>
        /// 单次读取缓存大小
        /// </summary>
        public int WriteOnceBufferSize { get; set; } = 1024;

        /// <summary>
        ///  发送信息
        /// </summary>
        private void Wirte()
        {
            if (WriteQueue.Count <= 0)
            {
                return;
            }
            WriteOffset = 0;
            try
            {
                m_socket.BeginSend(BitConverter.GetBytes(WriteQueue.Peek().Length), 0, sizeof(UInt32), SocketFlags.None, (res) => {
                    if (!m_socket.Connected)
                    {
                        return;
                    }
                    WriteBody();
                }, m_socket);
            }
            catch (Exception e)
            {
                UnityEngine.Debug.Log("SocketException:" + e.ToString());
                OnError?.Invoke(e);
            }
        }

        private void WriteBody()
        {
            try
            {
                int writelength = WriteQueue.Peek().Length - WriteOffset;
                if (writelength >= WriteOnceBufferSize) writelength = WriteOnceBufferSize;
                m_socket.BeginSend(WriteQueue.Peek(), WriteOffset, writelength, SocketFlags.None, (res) => {
                    if (!m_socket.Connected)
                    {
                        return;
                    }

                    try
                    {
                        WriteOffset += m_socket.EndSend(res);
                        if (WriteOffset >= WriteQueue.Peek().Length)
                        {
                            WriteQueue.Dequeue();
                            Wirte();
                        }
                        else
                        {
                            WriteBody();
                        }
                    }
                    catch (Exception e)
                    {
                        UnityEngine.Debug.LogErrorFormat("Exception msg:{0}", e.Message);
                        OnError?.Invoke(e);
                    }
                }, m_socket);
            }
            catch (SocketException e)
            {
                UnityEngine.Debug.Log("SocketException:" + e.ToString());
                //gLog.Log(Log.LogLevel.Debug, "\nResponseCallback Exception raised!");
                //gLog.Log(Log.LogLevel.Debug, "\nMessage:{0}", e.Message);
                //gLog.Log(Log.LogLevel.Debug, "\nStatus:{0}", e.Status);
                OnError?.Invoke(e);
            }
        }

        #endregion Write

        #region Read

        private byte[] ReadBuffer { get; set; }

        /// <summary>
        /// 读取偏移值
        /// </summary>
        private int ReadOffset { get; set; } = 0;

        /// <summary>
        /// 单次读取缓存大小
        /// </summary>
        public int ReadOnceBuffSize { get; set; } = 1024;

        /// <summary>
        /// 接受信息
        /// </summary>
        /// <returns></returns>
        private void Read()
        {
            ReadOffset = 0;
            try
            {
                byte[] bodylength = new byte[sizeof(UInt32)];
                m_socket.BeginReceive(bodylength, 0, bodylength.Length, SocketFlags.None, (res) => {
                    if (!m_socket.Connected)
                    {
                        return;
                    }
                    ReadBuffer = new byte[BitConverter.ToUInt32(bodylength, 0)];
                    ReadBody();
                }, m_socket);
            }
            catch (Exception e)
            {
                UnityEngine.Debug.LogErrorFormat("Exception msg:{0}", e.Message);
            }
        }

        private void ReadBody()
        {
            try
            {
                int readlength = ReadBuffer.Length - ReadOffset;
                if (readlength >= ReadOnceBuffSize) { readlength = ReadOnceBuffSize; }
                m_socket.BeginReceive(ReadBuffer, ReadOffset, readlength, SocketFlags.None, (res) => {
                    if (!m_socket.Connected)
                    {
                        return;
                    }

                    try
                    {
                        ReadOffset += m_socket.EndReceive(res);
                        if (ReadOffset >= ReadBuffer.Length)
                        {
                            OnReceive(ReadBuffer);
                            Read();
                        }
                        else
                        {
                            ReadBody();
                        }
                    }
                    catch (Exception e)
                    {
                        UnityEngine.Debug.LogErrorFormat("Exception msg:{0}", e.Message);
                        OnError?.Invoke(e);
                    }
                }, m_socket);
            }
            catch (Exception e)
            {
                UnityEngine.Debug.LogErrorFormat("Exception msg:{0}", e.Message);
                OnError?.Invoke(e);
            }
        }

        #endregion Read

        public void Start()
        {
            Read();
        }

        public void Stop()
        {
            m_socket.Close();
        }

        public bool Deliver(byte[] buffer)
        {
            bool iswrite = WriteQueue.Count <= 0;
            WriteQueue.Enqueue(buffer);
            if(iswrite)
            {
                Wirte();
            }
            return true;
        }
    }
}
